package com.jhf.youke.gateway.config.filter;

import cn.hutool.core.convert.Convert;
import cn.hutool.json.JSONUtil;
import com.jhf.youke.core.entity.Message;
import com.jhf.youke.core.utils.AddressUtils;
import com.jhf.youke.core.utils.CacheUtils;
import com.jhf.youke.core.utils.IdGen;
import com.jhf.youke.core.utils.IpUtils;
import com.jhf.youke.gateway.entity.BaseLog;
import com.jhf.youke.gateway.entity.PermissionsSetVo;
import com.jhf.youke.gateway.utils.BaseUtils;
import com.jhf.youke.gateway.utils.MqUtils;
import com.jhf.youke.gateway.utils.RequestUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @note 目前只记录了request方式为POST请求的方式
 * @author rhj
 */
@Component
public class RequestRecorderGlobalFilter implements GlobalFilter, Ordered {

    private final String http = "http";
    private final String https = "https";
    private final String application = "application";
    private final String text = "text";

    private Logger logger = LoggerFactory.getLogger(RequestRecorderGlobalFilter.class);

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest originalRequest = exchange.getRequest();
        URI originalRequestUrl = originalRequest.getURI();
        //只记录http的请求
        String scheme = originalRequestUrl.getScheme();

        if(true){
            return chain.filter(exchange);
        }

        if ((!http.equals(scheme) && !https.equals(scheme))) {
            return chain.filter(exchange);
        }

        //我自己的log实体
        BaseLog baseLog = null;

        String title = null;
        String url = exchange.getRequest().getPath().toString();

        try {
            Map<String, Object> data = BaseUtils.getPermissionsListToMap();
            String json = JSONUtil.toJsonStr(data.get("data"));
            Map<String, Object> permissions = JSONUtil.toBean(json, Map.class);

            boolean isAuth = false;
            List<PermissionsSetVo> list = null;
            list = JSONUtil.toList(JSONUtil.parseArray(permissions.get(url)), PermissionsSetVo.class);
            PermissionsSetVo permission = CollectionUtils.isEmpty(list) ? new PermissionsSetVo() : list.get(0);

            if (permission != null) {
                isAuth = true;
            }

            if (isAuth) {
                String name = permission.getName();
                baseLog = new BaseLog();
                baseLog.setId(IdGen.id());
                baseLog.setOperation(name);
                baseLog.setRemark(name);
            }
        } catch (Exception e) {
            logger.error(e.getMessage());
        }

        // 返回解码
        RecorderServerHttpResponseDecorator response = new RecorderServerHttpResponseDecorator(exchange.getResponse(), baseLog);
        //请求解码
        RecorderServerHttpRequestDecorator recorderServerHttpRequestDecorator = new RecorderServerHttpRequestDecorator(exchange.getRequest());
        //增加过滤拦截吧
        ServerWebExchange ex = exchange.mutate()
                .request(recorderServerHttpRequestDecorator)
                .response(response)
                .build();

        response.beforeCommit(() -> Mono.defer(() -> printLog(response)));

        return recorderOriginalRequest(ex, baseLog, title)
                .then(chain.filter(ex))
                .then();
    }


    private Mono<Void> recorderOriginalRequest(ServerWebExchange exchange, BaseLog baseLog, String title) {
        ServerHttpRequest request = exchange.getRequest();
        Mono<Void> result = recorderRequest(request, baseLog, title);
        try {
            if (baseLog != null) {
                baseLog.setUpdateType(1);
                Message message = new Message();
                message.setTopic("baseLogConsume");
                message.setTag("all");
                message.setMessages(baseLog);
                String data = JSONUtil.toJsonStr(MqUtils.send(message));
                logger.info("添加操作日志：" + data);
            }
        } catch (Exception e) {
            logger.error("保存请求参数出现错误, e->{}", e.getMessage());
        }
        return result;
    }

    /**
     * 记录原始请求逻辑
     */
    private Mono<Void> recorderRequest(ServerHttpRequest request, BaseLog baseLog, String title) {

        var token = request.getHeaders().getFirst("token");

        if (!StringUtils.isEmpty(token)) {
            String json = CacheUtils.get(token);
            try {
                //当前在登录，则记录用户ID跟名称
                Map<String, Object> map = JSONUtil.toBean(json, Map.class);
                if (map != null) {
                    Long userId = Long.valueOf(map.get("id").toString());
                    String loginName = map.get("loginName") != null ? map.get("loginName").toString() : "";
                    String name = map.get("name") != null ? map.get("name").toString() : "";
                    baseLog.setUserId(userId);
                    baseLog.setUserName(loginName);
                    if (!StringUtils.isEmpty(name)) {
                        baseLog.setUserName(baseLog.getUserName() + "(" + name + ")");
                    }
                    baseLog.setUserType(Convert.toInt(map.get("type")));
                }
            } catch (Exception e) {
            }
        }

        String url = request.getPath().toString();
        HttpMethod method = request.getMethod();
        HttpHeaders headers = request.getHeaders();
        if (baseLog != null) {
            baseLog.setLoginIp(RequestUtils.getIpAddress(request));
            baseLog.setAddress(AddressUtils.getRealAddressByIp(baseLog.getLoginIp()));
            baseLog.setSystemName(IpUtils.getOsInfo(request));
            baseLog.setBrowser(IpUtils.getBrowserInfo(request));
            baseLog.setUrl(url);
            baseLog.setMethod(method.toString());
            baseLog.setCreateTime(new Date());
            baseLog.setStatus(0);

        }

        Charset bodyCharset = null;
        if (hasBody(method)) {
            long length = headers.getContentLength();
            if (length <= 0) {
            } else {
                MediaType contentType = headers.getContentType();
                if (contentType == null) {
                } else if (!shouldRecordBody(contentType)) {
                } else {
                    bodyCharset = getMediaTypeCharset(contentType);
                }
            }
        }
        if (bodyCharset != null) {
            return doRecordReqBody(request.getBody(), bodyCharset, baseLog)
                    .then(Mono.defer(() -> {
                        return Mono.empty();
                    }));
        } else {
            return Mono.empty();
        }
    }

    /** 日志输出返回值 **/
    private Mono<Void> printLog(ServerHttpResponse response) {
        HttpStatus statusCode = response.getStatusCode();
        assert statusCode != null;
        return Mono.empty();
    }


    @Override
    public int getOrder() {
        //在GatewayFilter之前执行
        return -1;
    }

    private boolean hasBody(HttpMethod method) {
        //只记录这3种谓词的body
//        if (method == HttpMethod.POST || method == HttpMethod.PUT || method == HttpMethod.PATCH)
        return true;
//        return false;
    }

    /**
     * 记录简单的常见的文本类型的request的body和response的body
     *
     * @param contentType 内容类型
     * @return boolean
     */
    private boolean shouldRecordBody(MediaType contentType) {
        String type = contentType.getType();
        String subType = contentType.getSubtype();
        if (application.equals(type)) {
            return "json".equals(subType) || "x-www-form-urlencoded".equals(subType) || "xml".equals(subType) || "atom+xml".equals(subType) || "rss+xml".equals(subType);
        } else if (text.equals(type)) {
            return true;
        }
        //暂时不记录form
        return false;
    }

    /**
     * 获取请求的参数
     *
     * @param body    身体
     * @param charset 字符集
     * @param baseLog 基地日志
     * @return {@link Mono}<{@link Void}>
     */
    private Mono<Void> doRecordReqBody(Flux<DataBuffer> body, Charset charset, BaseLog baseLog) {
        return DataBufferUtils.join(body).doOnNext(buffer -> {
            CharBuffer charBuffer = charset.decode(buffer.asByteBuffer());
            //记录我实体的请求体
            if (baseLog != null) {
                baseLog.setRequestParam(charBuffer.toString());
            }
//            logBuffer.append(charBuffer.toString());
            DataBufferUtils.release(buffer);
        }).then();
    }

    private Charset getMediaTypeCharset(@Nullable MediaType mediaType) {
        if (mediaType != null && mediaType.getCharset() != null) {
            return mediaType.getCharset();
        } else {
            return StandardCharsets.UTF_8;
        }
    }
}
